package com.fzk.core;

import com.fzk.log.Logger;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Set;

/**
 * 保存文件元信息储存文件的记录点，用于重启恢复
 * todo 新增后台接口获取当前checkpoint
 *
 * @author fzk
 * @datetime 2023-03-17 20:01
 */
public class FileCheckPoint implements AutoCloseable {
    private final FileChannel fileChannel;
    private volatile long metaFileCheckPoint;// 文件元信息储存文件检查点
    private static final long META_FILE_POSITION = 0;// 文件元信息储存文件检查点偏移量

    public FileCheckPoint(Path path) {
        try {
            boolean exists = true;
            if (Files.notExists(path)) {
                exists = false;
                Files.createFile(path);
            }
            fileChannel = FileChannel.open(path, Set.of(StandardOpenOption.WRITE, StandardOpenOption.READ));
            if (!exists) {
                updateMetaCheckPoint(0L);
            } else {
                this.metaFileCheckPoint = read(META_FILE_POSITION);
            }
            Logger.info(String.format("checkpoint文件 %s 初始化成功，metaFileCheckPoint为%d", path.getFileName(), this.metaFileCheckPoint));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateMetaCheckPoint(long checkPoint) throws IOException {
        this.metaFileCheckPoint = checkPoint;// 先更新内存
        write(META_FILE_POSITION, this.metaFileCheckPoint);// 再更新文件
    }


    public long getMetaCheckPoint() {
        return this.metaFileCheckPoint;
    }

    private void write(long position, long checkPoint) throws IOException {
        fileChannel.write(ByteBuffer.wrap(longToBytes(checkPoint)), position);
    }

    private long read(long position) throws IOException {
        if (fileChannel.size() < (position + 8)) {// 不够读直接返回0
            return 0L;
        }
        ByteBuffer buf = ByteBuffer.allocate(8);
        fileChannel.read(buf, position);// 应该能一次性读满8byte
        buf.flip();// 翻转
        return buf.getLong(0);
    }


    private byte[] longToBytes(long val) {
        byte[] bytes = new byte[8];
        for (int i = 7; i >= 0; i--) {
            bytes[i] = (byte) val;
            val >>= 8;
        }
        return bytes;
    }

    @Override
    public void close() throws IOException {
        fileChannel.close();
    }
}
